﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace HobSoft.SoccerBet2014.Domain.Implementation.Local
{
    using Domain.Common.SiteContext;
    using Domain.Models.Site;
    public class CookiesManager : ReadWriteBinaryRepository<AccountCookie, long>, ICookiesManager
    {
        private static long _lastKey = DateTime.Now.Ticks;
        private static object _keyLock = new object();
        protected static long GenKey()
        {
            long result = 0;
            lock (_keyLock)
            {
                _lastKey++;
                result = _lastKey;
            }
            return result;
        }


        public CookieContainer GetCookies(byte bookerId, string account, Uri url)
        {
            List<AccountCookie> httpOnlyCookies = new List<AccountCookie>();
            CookieContainer cookies = new CookieContainer();
            var accountCookies = BinModels.Values.Where(c => c.BmID == bookerId && c.Account == account);
            foreach (var ack in accountCookies)
            {
                if (ack.Cookie.HttpOnly)
                {
                    ack.Cookie.Expires = DateTime.Now.AddYears(1);
                    ack.LastUpdate = DateTime.Now;
                    //BinModels.TryUpdate(ack.Key, ack, null);
                }
                if (url.Host.EndsWith(ack.Cookie.Domain, StringComparison.OrdinalIgnoreCase))
                    cookies.Add(url, ack.Cookie.ToCookie());
            }
            return cookies;
        }

        public Cookie GetCookie(byte bookerId, string account, string cookieName, Uri url)
        {
            var accountCookie = BinModels.Values.FirstOrDefault(c => c.BmID == bookerId
                && c.Account == account
                && c.Cookie.Name == cookieName
                && url.Host.EndsWith(c.Cookie.Domain, StringComparison.OrdinalIgnoreCase));

            if (accountCookie == null)
            {
                return null;
            }
            if (accountCookie.Cookie.HttpOnly)
            {
                accountCookie.Cookie.Expires = DateTime.Now.AddYears(1);
                accountCookie.LastUpdate = DateTime.Now;
            }

            return accountCookie.Cookie.ToCookie();
        }

        public Cookie GetHttpCookie(byte bookerId, string account, string cookieName, DateTime updateTime, IEnumerable<string> exceptDomain)
        {
            var accountCookie = BinModels.Values.Where(c => c.BmID == bookerId
                                            && c.Account == account
                                            && c.Cookie.Name == cookieName
                                            && c.LastUpdate > updateTime
                                            && !exceptDomain.Contains(c.Cookie.Domain))
                                            .OrderByDescending(c => c.LastUpdate)
                                            .FirstOrDefault();
            if (accountCookie == null) return null;
            if (accountCookie.Cookie.HttpOnly)
            {
                accountCookie.Cookie.Expires = DateTime.Now.AddYears(1);
                accountCookie.LastUpdate = DateTime.Now;
            }
            return accountCookie.Cookie.ToCookie();
        }

        public Cookie GetHttpCookie(byte bookerId, string account, string cookieName, string domain)
        {
            var accountCookie = BinModels.Values.Where(c => c.BmID == bookerId
                                            && c.Account == account
                                            && c.Cookie.Name == cookieName
                                            && c.Cookie.Domain.Equals(domain, StringComparison.OrdinalIgnoreCase))
                                            .OrderByDescending(c => c.LastUpdate)
                                            .FirstOrDefault();
            if (accountCookie == null) return null;
            if (accountCookie.Cookie.HttpOnly)
            {
                accountCookie.Cookie.Expires = DateTime.Now.AddYears(1);
                accountCookie.LastUpdate = DateTime.Now;
            }
            return accountCookie.Cookie.ToCookie();
        }

        public void SetCookies(byte bookerId, string account, Cookie cookie)
        {
            var accountCookie = BinModels.Values.FirstOrDefault(c => c.BmID == bookerId
                && c.Account == account
                && c.Cookie.Name == cookie.Name
                && (
                c.Cookie.Domain.EndsWith(cookie.Domain, StringComparison.OrdinalIgnoreCase)
                || cookie.Domain.EndsWith(c.Cookie.Domain, StringComparison.OrdinalIgnoreCase)));
 
            if (accountCookie == null)
            {
                accountCookie = new AccountCookie()
                {
                    Key = GenKey(),
                    BmID = bookerId,
                    Account = account,
                    LastUpdate = DateTime.Now,
                    Cookie = new PersistentCookie(cookie)
                };
            }
            else
            {
                accountCookie.Cookie = new PersistentCookie(cookie);
            }
            BinModels.AddOrUpdate(accountCookie.Key, accountCookie, (k, u) => accountCookie);
        }

        public void SetCookies(byte bookerId, string account, CookieCollection cookies)
        {
            foreach (Cookie cookie in cookies)
            {
                var existCookie = BinModels.Values.FirstOrDefault(c => c.BmID == bookerId && c.Account == account
                                                            && c.Cookie.Name == cookie.Name
                                                            && (
                                                            c.Cookie.Domain.EndsWith(cookie.Domain, StringComparison.OrdinalIgnoreCase)
                                                            || cookie.Domain.EndsWith(c.Cookie.Domain, StringComparison.OrdinalIgnoreCase)
                                                            ));
                if (existCookie == null)
                {
                    AccountCookie newCookie = new AccountCookie()
                    {
                        Key = GenKey(),
                        BmID = bookerId,
                        Account = account,
                        LastUpdate = DateTime.Now,
                        Cookie = new PersistentCookie(cookie)
                    };
                    BinModels.AddOrUpdate(newCookie.Key, newCookie, (k, c) => newCookie);
                }
                else
                {
                    existCookie.Cookie = new PersistentCookie(cookie);
                    existCookie.LastUpdate = DateTime.Now;
                    //BinModels.AddOrUpdate(existCookie.Key, existCookie, (k, c) => existCookie);
                }
            }
        }

        public void DeleteCookies(byte bookerId, string account, string cookieName, IEnumerable<string> exceptDomain)
        {
            var delCookieKeys = BinModels.Where(c => c.Value.BmID == bookerId
                                                && c.Value.Account == account
                                                && c.Value.Cookie.Name == cookieName
                                                && !exceptDomain.Contains(c.Value.Cookie.Domain))
                                                .Select(c => c.Key).ToArray();
            foreach (var key in delCookieKeys)
            {
                AccountCookie del;
                BinModels.TryRemove(key, out del);
            }

        }

        public void Clear(byte bookerId, string account)
        {
            var keys = BinModels.Where(c => c.Value.BmID == bookerId && c.Value.Account == account).Select(c => c.Key).ToArray();
            foreach (var key in keys)
            {
                AccountCookie del;
                BinModels.TryRemove(key, out del);
            }
        }

        protected override Func<AccountCookie, long> FindKey
        {
            get { return pc => pc.Key; }
        }
    }
}
